using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Globalization;
using ResourceBlender.DAL;
using ResourceBlender.Core;

namespace ResourceBlender.Express {
    /// <summary>
    /// Allows a user to select an application, languages and bundles.
    /// </summary>
    public partial class ApplicationSelector : UserControl, INotifyPropertyChanged {
        /// <summary>
        /// The mode of selection which the ApplicationSelector allows.
        /// </summary>
        public enum SelectionMode {
            /// <summary>
            /// Allows a single application, multiple bundles and languages to be selected.
            /// </summary>
            SingleApplicationBundlesLanguages,
            /// <summary>
            /// Allows a single application and multiple bundles to be selected.
            /// </summary>
            SingleApplicationBundles,
            /// <summary>
            /// Allows multiple applications, bundles and languages to be selected.
            /// </summary>
            MultiApplicationBundlesLanguages
        }

        public delegate void IsBusyDelegate(bool isBusy);
        /// <summary>
        /// Occurs when the control is busy refreshing the list of applications.
        /// </summary>
        public event IsBusyDelegate IsBusy;

        public event EventHandler ApplicationsChanged;

        public static readonly DependencyProperty ModeProperty = DependencyProperty.Register("Mode", typeof(SelectionMode), typeof(ApplicationSelector));
        /// <summary>
        /// Gets or sets the selection mode which the ApplicationSelector allows.
        /// </summary>
        /// <value>The selection mode.</value>
        public SelectionMode Mode {
            get { return (SelectionMode)base.GetValue(ModeProperty); }
            set { 
                base.SetValue(ModeProperty, value);
                switch(value) {
                    case SelectionMode.SingleApplicationBundlesLanguages:
                        lstApplication.Style = this.FindResource("RadioListTemplate") as Style;
                        grdMain.ColumnDefinitions[0].Width = new GridLength(3, GridUnitType.Star);
                        grdMain.ColumnDefinitions[1].Width = new GridLength(3, GridUnitType.Star);
                        grdMain.ColumnDefinitions[2].Width = new GridLength(3, GridUnitType.Star);
                        this.MinWidth = 440;
                        break;
                    case SelectionMode.SingleApplicationBundles:
                        lstApplication.Style = this.FindResource("RadioListTemplate") as Style;
                        grdMain.ColumnDefinitions[0].Width = new GridLength(2, GridUnitType.Star);
                        grdMain.ColumnDefinitions[1].Width = new GridLength(2, GridUnitType.Star);
                        grdMain.ColumnDefinitions[2].Width = new GridLength(0);
                        this.MinWidth = 280;
                        break;
                    case SelectionMode.MultiApplicationBundlesLanguages:
                        lstApplication.Style = this.FindResource("CheckBoxListTemplate") as Style;
                        grdMain.ColumnDefinitions[0].Width = new GridLength(3, GridUnitType.Star);
                        grdMain.ColumnDefinitions[1].Width = new GridLength(3, GridUnitType.Star);
                        grdMain.ColumnDefinitions[2].Width = new GridLength(3, GridUnitType.Star);
                        this.MinWidth = 440;
                        break;
                }
            }
        }

        public static readonly DependencyProperty SelectAllOnChangeProperty = DependencyProperty.Register("SelectAllOnChange", typeof(bool), typeof(ApplicationSelector), new PropertyMetadata(true));
        /// <summary>
        /// Gets or sets a value indicating whether all bundles and languages should be selected when the application selection changes.
        /// </summary>
        /// <value><c>true</c> if all bundles and languages should be selected when the application selection changes, otherwise <c>false</c>.</value>
        public bool SelectAllOnChange {
            get { return (bool)base.GetValue(SelectAllOnChangeProperty); }
            set { base.SetValue(SelectAllOnChangeProperty, value); }
        }

        private Window parentWindow;

        /// <summary>
        /// Gets the selected applications.
        /// </summary>
        /// <value>The selected applications.</value>
        public List<ObservableApplication> SelectedApplications {
            get {
                List<ObservableApplication> selectedApps = new List<ObservableApplication>();
                foreach(ObservableApplication app in lstApplication.SelectedItems) {
                    selectedApps.Add(app);
                }
                return selectedApps;
            }
            set {
                if(this.IsLoaded) {
                    if(Mode != SelectionMode.SingleApplicationBundles && Mode != SelectionMode.SingleApplicationBundlesLanguages) {
                        throw new NotSupportedException("SelectedApplications can only be set when Mode is SingleApplication");
                    }
                    //lstApplication.SelectedItem = value.Count > 0 ? value[0] : null;
                    lstApplication.SelectedValue = value.Count > 0 ? value[0].ID : null;
                } else {
                    this.ApplicationsChanged += (s, e) => {
                        SelectedApplications = value;
                    };
                }
            }
        }

        /// <summary>
        /// Gets the selected bundles.
        /// </summary>
        /// <value>The selected bundles.</value>
        public ObservableSortedDictionary<string, string> SelectedBundles {
            get {
                ObservableSortedDictionary<string, string> items = new ObservableSortedDictionary<string, string>(new Comparers.DictionaryValueComparer(), new Comparers.StringEqualityComparer());
                foreach(KeyValuePair<string, string> itm in lstBundles.SelectedItems) {
                    items.Add(itm.Key, itm.Value);
                }
                return items;
            }
        }

        /// <summary>
        /// Gets the selected languages.
        /// </summary>
        /// <value>The selected languages.</value>
        public ObservableSortedDictionary<string, CultureInfo> SelectedLanguages {
            get {
                ObservableSortedDictionary<string, CultureInfo> items = new ObservableSortedDictionary<string, CultureInfo>(new Comparers.CultureInfoEnglshNameComparer(), new Comparers.StringEqualityComparer());
                foreach(KeyValuePair<string, CultureInfo> itm in lstLanguages.SelectedItems) {
                    items.Add(itm.Key, itm.Value);
                }
                return items;
            }
        }

        /// <summary>
        /// Initializes a new <see cref="ApplicationSelector"/> to select a single application with bundles and languages.
        /// </summary>
        public ApplicationSelector() 
            :this(SelectionMode.SingleApplicationBundlesLanguages) { }

        /// <summary>
        /// Initializes a new <see cref="ApplicationSelector"/>.
        /// </summary>
        /// <param name="mode">The selection mode.</param>
        public ApplicationSelector(SelectionMode mode) {
            InitializeComponent();
            this.Mode = mode;
        }

        /// <summary>
        /// Finds the parent window and loads the list of applications.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void UserControl_Loaded(object sender, RoutedEventArgs e) {
            parentWindow = Window.GetWindow(this);
            RefreshApplications(this, null);
        }

        /// <summary>
        /// Refreshes the list of applications using a background thread.
        /// </summary>
        public void RefreshApplications() {
            NavigationCommands.Refresh.Execute(null, null);
        }

        /// <summary>
        /// Refreshes the list of applications using a background thread.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void RefreshApplications(object sender, RoutedEventArgs e) {
            BackgroundWorker worker = new BackgroundWorker();
            worker.WorkerReportsProgress = true;
            worker.DoWork += (s, dwe) => {
                worker.ReportProgress(1, true);
                try {
                    List<RBApplication> apps = ResourceBlenderDAL.GetApplications(true);
                    List<ObservableApplication> observableApps =new List<ObservableApplication>();
                    foreach(var app in apps) {
                        observableApps.Add(new ObservableApplication(app));
                    }
                    dwe.Result = observableApps;
                } catch(Exception ex) {
                    dwe.Result = "Error connecting to the database: " + ex.Message;
                }
            };
            worker.ProgressChanged += (s, pce) => {
                if(pce.UserState is bool && IsBusy != null) {
                    IsBusy((bool)pce.UserState);
                }
            };
            worker.RunWorkerCompleted += (s, rwe) => {
                if(IsBusy != null) {
                    IsBusy(false);
                }
                if(rwe.Result != null && rwe.Result is string) {
                    DialogBox.Show(parentWindow, rwe.Result.ToString(), "Database Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                } else {
                    List<ObservableApplication> observableApps = rwe.Result as List<ObservableApplication>;
                    observableApps.Sort();
                    DataContext = observableApps;
                    if(observableApps.Count > 0) {
                        lstApplication.SelectedIndex = 0;
                    }
                    if(ApplicationsChanged != null) {
                        ApplicationsChanged(this, RoutedEventArgs.Empty);
                    }
                }
            };
            worker.RunWorkerAsync();
        }

        /// <summary>
        /// Selects all languages and bundles for the newly selected application then fires the SelectionChanged event
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void lstApplication_SelectionChanged(object sender, RoutedEventArgs e) {
            if(Mode == SelectionMode.MultiApplicationBundlesLanguages) {
                ObservableSortedDictionary<string, string> allBundles = new ObservableSortedDictionary<string, string>(new Comparers.DictionaryValueComparer(), new Comparers.StringEqualityComparer());
                ObservableSortedDictionary<string, CultureInfo> allLanguages = new ObservableSortedDictionary<string, CultureInfo>(new Comparers.CultureInfoEnglshNameComparer(), new Comparers.StringEqualityComparer());
                foreach(ObservableApplication app in lstApplication.SelectedItems) {
                    foreach(KeyValuePair<string, string> bundle in app.Bundles) {
                        allBundles.Add(bundle.Key, bundle.Value);
                    }
                    foreach(string code in app.Languages) {
                        if(allLanguages.ContainsKey(code)) {
                            continue;
                        }
                        CultureInfo info = Utils.GetCultureInfo(code);
                        if(code != null) {
                            allLanguages.Add(code, info);
                        }
                    }
                }
                lstBundles.ItemsSource = allBundles;
                lstLanguages.ItemsSource = allLanguages;
            }
            if(SelectAllOnChange) {
                lstBundles.SelectAll();
                lstLanguages.SelectAll();
            }
            OnPropertyChanged("SelectedApplication");
        }

        /// <summary>
        /// Fires the SelectionChanged event
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void lstBundles_SelectionChanged(object sender, RoutedEventArgs e) {
            OnPropertyChanged("SelectedBundles");
        }

        /// <summary>
        /// Fires the SelectionChanged event
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
        private void lstLanguages_SelectionChanged(object sender, RoutedEventArgs e) {
            OnPropertyChanged("SelectedLanguages");
        }

        /// <summary>
        /// Validates the user's selection to check at least one application, bundle and language are selected. Providing feedback (MessageBoxes and changing the input focus) if necessary.
        /// </summary>
        /// <param name="provideFeedback"><c>true</c> if feedback (MessageBoxes and changing the input focus) should be provided.</param>
        /// <returns></returns>
        public bool ValidateSelection(bool provideFeedback) {
            bool valid = true;
            if(lstApplication.SelectedItems.Count == 0) {
                if(provideFeedback) {
                    DialogBox.Show(parentWindow, "No application was selected", "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                }
                valid = false;
            } else if(lstBundles.SelectedItems.Count == 0) {
                if(provideFeedback) {
                    DialogBox.Show(parentWindow, "No bundles were selected", "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                }
                valid = false;
            } else if(lstLanguages.SelectedItems.Count == 0 && Mode == SelectionMode.SingleApplicationBundlesLanguages) {
                if(provideFeedback) {
                    DialogBox.Show(parentWindow, "No languages were selected", "Error", DialogBox.DialogBoxType.OK, DialogBox.DialogBoxIcon.Error);
                }
                valid = false;
            }
            return valid;
        }

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string strPropertyName) {
            if(PropertyChanged != null) {
                PropertyChanged(this, new PropertyChangedEventArgs(strPropertyName));
            }
        }
    }
}
